use crate::matrix;
use crate::symbols::symbols::SymbolicExp::{Number, Symbol};
use crate::symbols::traits::Derivate;
use crate::vector::matrix::Matrix;
use crate::vector::vector::Vector;

#[test]
fn create_matrix() {
    let x = Symbol("x".to_string());
    let y = Symbol("y".to_string());
    let mut mat = Matrix::new(4, 4);
    let data = vec![
        Number(0.0),
        0.0.into(),
        x,
        0.0.into(),
        2.0.into(),
        2.0.into(),
        0.0.into(),
        2.0.into(),
        3.0.into(),
        3.0.into(),
        y,
        4.0.into(),
        5.0.into(),
        5.0.into(),
        5.0.into(),
        5.0.into(),
    ];
    mat.set_data(data);
    println!("{:?}", mat);
    let identity_mat = mat.identity();
    println!("{:?}", identity_mat);
}

#[test]
fn add_matrix() {
    let x = Symbol("x".to_string());
    let y = Symbol("y".to_string());
    let mut mat1 = Matrix::new(2, 4);
    let data1 = vec![
        Number(1.0),
        x,
        5.0.into(),
        7.0.into(),
        2.0.into(),
        4.0.into(),
        6.0.into(),
        8.0.into(),
    ];
    mat1.set_data(data1);
    let mut mat2 = Matrix::new(2, 4);
    let data2 = vec![
        Number(4.0),
        y,
        1.0.into(),
        4.0.into(),
        5.0.into(),
        3.0.into(),
        1.0.into(),
        6.0.into(),
    ];
    mat2.set_data(data2);
    println!("{:?}", mat1 + mat2);
}

#[test]
fn sub_matrix() {
    let x = Symbol("x".to_string());
    let y = Symbol("y".to_string());
    let mut mat1 = matrix!(2, 4);
    let data1 = vec![
        x,
        3.0.into(),
        5.0.into(),
        7.0.into(),
        2.0.into(),
        4.0.into(),
        6.0.into(),
        8.0.into(),
    ];
    mat1.set_data(data1);
    let mut mat2 = matrix!(2, 4);
    let data2 = vec![
        y,
        3.0.into(),
        1.0.into(),
        4.0.into(),
        5.0.into(),
        3.0.into(),
        1.0.into(),
        6.0.into(),
    ];
    mat2.set_data(data2);
    println!("{:?}", mat1 - mat2);
}

#[test]
fn mul_matrix_vector() {
    let mut mat = matrix!(3, 4);
    let x = Symbol("x".to_string());
    let y = Symbol("x".to_string());
    let data = vec![
        x,
        3.0.into(),
        5.0.into(),
        7.0.into(),
        11.0.into(),
        13.0.into(),
        17.0.into(),
        19.0.into(),
        23.0.into(),
        29.0.into(),
        31.0.into(),
        37.0.into(),
    ];
    mat.set_data(data);

    println!("{:?}", mat);

    let vector = Vector::new(vec![y, 2.0.into(), 3.0.into(), 4.0.into()]);

    let vector1 = mat * vector;
    println!("{:?}", vector1);
}

#[test]
fn mul_matrix_matrix() {
    let mut a = matrix!(2, 3);
    let x = Symbol("x".to_string());
    let y = Symbol("x".to_string());
    let a_data = vec![
        x,
        2.0.into(),
        3.0.into(),
        4.0.into(),
        5.0.into(),
        6.0.into(),
    ];

    let mut b = matrix!(3, 2);
    let b_data = vec![
        y,
        8.0.into(),
        9.0.into(),
        10.0.into(),
        11.0.into(),
        12.0.into(),
    ];

    a.set_data(a_data);
    b.set_data(b_data);

    println!("{:?}", a * b);
}

#[test]
fn transpose() {
    let mut mat1 = matrix!(2, 4);
    let x = Symbol("x".to_string());
    let y = Symbol("x".to_string());
    let data1 = vec![
        x,
        3.0.into(),
        5.0.into(),
        7.0.into(),
        y,
        4.0.into(),
        6.0.into(),
        8.0.into(),
    ];
    mat1.set_data(data1);
    let mat2 = mat1.transpose();
    let mat3 = mat2.transpose();
    println!("{:?}", mat2);
    println!("{:?}", mat3);
}

#[test]
fn elimination() {
    let mut a = matrix!(3, 3);
    let x = Symbol("x".to_string());
    let a_data = vec![
        x,
        2.0.into(),
        1.0.into(),
        3.0.into(),
        8.0.into(),
        1.0.into(),
        0.0.into(),
        4.0.into(),
        1.0.into(),
    ];
    a.set_data(a_data);
    let mut b = a.identity();
    b.set_item(1, 0, -a.get_item(1, 0));

    println!("{:?}", b);

    let mut c = a.identity();
    c.set_item(2, 1, -a.get_item(2, 1));

    let n = c * b * a;
    println!("{:?}", n);
}

#[test]
fn replace_row() {
    let mut a = matrix!(3, 3);
    let x = Symbol("x".to_string());
    let a_data = vec![
        x,
        2.0.into(),
        1.0.into(),
        3.0.into(),
        8.0.into(),
        1.0.into(),
        0.0.into(),
        4.0.into(),
        1.0.into(),
    ];
    a.set_data(a_data);

    let b = a.replace_row(0, 1);
    println!("{:?}", b);
}

#[test]
fn replace_col() {
    let mut a = matrix!(3, 3);
    let x = Symbol("x".to_string());
    let a_data = vec![
        x.clone(),
        2.0.into(),
        1.0.into(),
        3.0.into(),
        x.clone(),
        1.0.into(),
        0.0.into(),
        4.0.into(),
        1.0.into(),
    ];
    a.set_data(a_data);

    let b = a.replace_col(1, 2);
    println!("{:?}", b);
}

#[test]
fn reciprocal() {
    let mut mat = matrix!(3, 3);
    let x = Symbol("x".to_string());
    let data = vec![
        x.clone(),
        2.0.into(),
        3.0.into(),
        0.0.into(),
        1.0.into(),
        4.0.into(),
        5.0.into(),
        6.0.into(),
        0.0.into(),
    ];
    mat.set_data(data);

    println!("{:?}", mat);
    let reciprocal_mat = mat.reciprocal();
    println!("{:?}", reciprocal_mat);
    println!("{:?}", reciprocal_mat * mat);

    let mut mat1 = matrix!(3, 3);
    let data1 = vec![
        x.clone(),
        (-1.0).into(),
        0.0.into(),
        1.0.into(),
        2.0.into(),
        1.0.into(),
        0.0.into(),
        (-1.0).into(),
        1.0.into(),
    ];
    mat1.set_data(data1);

    println!("{:?}", mat1);
    let reciprocal_mat1 = mat1.reciprocal();
    println!("{:?}", reciprocal_mat1);
    println!("{:?}", reciprocal_mat1 * mat1);
}

#[test]
fn lu() {
    let mut mat = matrix!(4, 4);
    let data = vec![
        1.0, 2.0, 4.0, 1.0, 2.0, -3.0, -1.0, -1.0, 1.0, 1.0, 4.0, 2.0, 3.0, 2.0, 1.0, -1.0,
    ];
    mat.set_data(data);

    let (l_mat, u_mat) = mat.lu();

    println!("{:?}", mat);
    println!("{:?}", l_mat);
    println!("{:?}", u_mat);
    println!("{:?}", l_mat * u_mat)
}

#[test]
fn matrix_derivate_from_symbols() {
    let mut mat = matrix!(3, 3);
    let x = Symbol("x".to_string());
    let y = Symbol("y".to_string());
    let symbols = x.clone() + y.clone();
    let data = vec![
        x.clone(),
        y.into(),
        3.0.into(),
        0.0.into(),
        1.0.into(),
        4.0.into(),
        5.0.into(),
        6.0.into(),
        0.0.into(),
    ];
    mat.set_data(data);

    println!("{:?}", mat.derivate_from_symbols(symbols));
}

#[test]
fn matrix_derivate_to_symbols() {
    let mut mat = matrix!(3, 3);
    let x = Symbol("x".to_string());
    let y = Symbol("y".to_string());

    let data = vec![
        x.clone() * y.clone(),
        x.clone() + y.clone(),
        3.0.into(),
        0.0.into(),
        1.0.into(),
        4.0.into(),
        5.0.into(),
        6.0.into(),
        0.0.into(),
    ];
    mat.set_data(data);

    println!("{:?}", mat.derivate_by_symbols(x.clone()));
}
